ts-res
Tiny library to improve error handling in TS using Rust-inspired types.
Contents
Installation
- Using npm -
npm install ts-res
- Using yarn -
yarn add ts-res
- Using pnpm -
pnpm add ts-res
Result Type
Result type is convenient to use as a return type of a method that may fail.
In Result<T, E>
:
T
specifies success result type, which can be any typeE
specifies error type, which is constrained by Error | string | void | undefined
.
Err()
and Ok()
are used as a return type enforcers.
function mayFail(isFailed: boolean): Result<number, Error> {
if (isFailed) {
return Err(new Error("Function failed"));
}
return Ok(123);
}
As a result we get an object which will either be {ok: true, data: T}
or {ok: false, error: E}
Note: Ok()
and Err()
can be used with no values if T
/E
were specified as void
or undefined
function getEmptyResult(isFailed: boolean): Result<void, void> {
if (isFailed) {
return Err();
}
return Ok();
}
Helper Methods
Result<T, E>
can be handled manually:
const result = mayFail(true);
if (!result.ok) {
throw result.error;
}
console.log(result.data);
However it's much easier to use helper methods to quickly handle the result:
throw(message?: string)
- throws an error (with an optional custom error message) or returns an unwrapped resultor(value: T)
- returns an unwrapped result or a back-up valueelse(callback: (error: E) => T)
- returns an unwrapped result or executes callback that returns back-up value which can be based on provided errorand(callback: (result: T) => Result<T, E>)
- handles a result in a callback while ignoring an error, returns the result allowing for chaining
mayFail(true).throw();
mayFail(false).throw();
mayFail(false).throw("My Message");
mayFail(true).or(100);
mayFail(false).or(100);
mayFail(true).else((error) => 200);
mayFail(false).else((error) => 200);
mayFail(true).and((result) => {
console.log(result);
});
mayFail(false).and((result) => {
console.log(result);
});
Examples
More elaborate examples:
- Use
throw()
to unwrap the value after parsing a number
function toNumber(str: string): Result<number, string> {
const result = Number(str);
if (isNaN(result)) {
return Err("Couldn't parse a string");
}
return Ok(result);
}
const myNumber: number = toNumber("Hello").throw();
const myNumber: number = toNumber("123").throw();
- Use
or()
to provide a back-up value while obtaining status code
function getStatusCode(statusCode: number): Result<number, string> {
if (statusCode > 200 && statusCode < 300) {
return Ok(statusCode);
}
return Err("Invalid status code");
}
function obtainStatus(): number {
return getStatusCode(response.statusCode).or(404);
}
- Use
else()
and CustomError
to provide a back-up value based on error type
enum ErrorType {
A,
B,
C,
}
class CustomError extends Error {
private errorType: ErrorType;
constructor(type: ErrorType) {
super();
this.errorType = type;
}
get type(): ErrorType {
return this.errorType;
}
}
function myFunction(): Result<void, CustomError> {
}
myFunction().else((error) => {
switch (error.type) {
case ErrorType.A:
return "a";
case ErrorType.B:
return "b";
case ErrorType.C:
return "c";
default:
break;
}
});
- Use
and()
to handle a result of writing data to localStorage
and ignore the error
function readLocalStorage(key: string): Result<string, void> {
const data = localStorage.getItem(key);
if (!data) {
return Err();
}
return Ok(data);
}
readLocalStorage.and((data) => {
const parsedData = JSON.parse(data);
console.log(parsedData);
})
- Use
and()
for handling http response data and chaining it with another method
type User = {
name: string;
age: number;
};
async function fetchUserData(): Promise<Result<User, Error>> {
const response = await fetch("https://api.example.com/user");
if (!response.ok) {
return Err(new Error("Failed to fetch user data"));
}
const data = await response.json();
return Ok(data);
}
const result = await fetchUserData();
result
.and((user) => {
console.log(user);
})
.else((error) => {
console.error(error);
});